package com.xhej.fund.service;

import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.csii.pe.common.util.BeanUtils;
import com.csii.pe.core.MultiBundlesClassLoader;
import com.csii.pe.dynamic.jnl.JNLDataService;
import com.csii.pe.dynamic.service.DataAccessCallbackService;
import com.csii.pe.dynamic.service.ProcessState;
import com.csii.pe.dynamic.service.RequestContext;
import com.csii.pe.dynamic.service.ResponseContext;
import com.csii.pe.transform.stream.extern.ExternUtil;

@SuppressWarnings({"unchecked","rawtypes"})
abstract public class FUNDCallbackService<RR,RS> extends DataAccessCallbackService<RR,RS>
{

	protected Class<RR> classRR;
	protected Class<RS> classRS;
	protected Log log = LogFactory.getLog(this.getClass());
	
	private String writeJnlSqlId;
	private String updateJnlSqlId;
	
	private JNLDataService jnlDataService;
	
	public String getUpdateJnlSqlId() {
		return updateJnlSqlId;
	}

	public void setUpdateJnlSqlId(String updateJnlSqlId) {
		this.updateJnlSqlId = updateJnlSqlId;
	}

	public String getWriteJnlSqlId() {
		return writeJnlSqlId;
	}

	public String getReadJnlSqlId() {
		return readJnlSqlId;
	}

	private String readJnlSqlId;
	
	public void setWriteJnlSqlId(String writeJnlSqlId) {
		this.writeJnlSqlId = writeJnlSqlId;
	}

	public void setReadJnlSqlId(String readJnlSqlId) {
		this.readJnlSqlId = readJnlSqlId;
	}

	
	public FUNDCallbackService()
	{
		
		classRR =  (Class<RR>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];

		classRS =  (Class<RS>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
	}

	
    public  RR prepare(Map<?, ?> dataMap) {
	
		if(log.isDebugEnabled())
		{
			log.debug(dataMap);
		}
		String RequestBean=(String) dataMap.get("RequestBean");
		ClassLoader pcl = this.getClass().getClassLoader();
		ClassLoader ccl = classRR.getClassLoader();
		ClassLoader bcl;
		if (pcl == ccl)
			bcl = pcl;
		else
			bcl = new MultiBundlesClassLoader(ccl, pcl);
		return  BeanUtils.map2Bean((Map) ExternUtil.parse(RequestBean, bcl), classRR );
		
	}

	protected RS notifyInit(RR request, RequestContext originRequestContext)
	{
		if(getWriteJnlSqlId()!=null)
		{
			//转换录入交易请求上下文数据
			Map  requestMap = BeanUtils.bean2Map(originRequestContext);
			
			//转换交易请求数据
			Map  dataMap =  BeanUtils.bean2MapRecurse(request);
			
			requestMap.putAll(dataMap);
			
			this.sqlMap.insert(getWriteJnlSqlId(),requestMap);	
		}else if (this.getJnlDataService() != null) {
            jnlDataService.write("P", originRequestContext, request);
        }
		getResponseContext().setResponseProcessState(ProcessState.I);
		return null;
				
	}
	
	protected RS notifyOther(ProcessState processState, RR request, RequestContext originRequestContext)
	{
		updateJnl(originRequestContext.getRequestJnlNo(),processState);
		getResponseContext().setResponseProcessState(processState);
		return null;
		
	}
	
	abstract protected RS notifySubmit(RR request, RequestContext originRequestContext);
	
	
	protected RS prepareResponse(Object routerResponse, ResponseContext routerResponseContext )
	{
    
		//一般情况下将路由服务返回上下文直接复制到整合平台服务的返回上下文
		//返回上下文可存在三种情形：
		// 1）业务系统成功处理，返回码为 ResponseContext.SUCCESS
		// 2) 业务系统拒绝， 返回码由对方系统定义
		// 3）业务系统拒绝但交易状态不确定， 返回码为ResponseContext.UNCERTAIN_ERROR
		
	
		if(routerResponseContext!=null)
		{
			if(!routerResponseContext.getResponseRejCode().equals(ResponseContext.SUCCESS))
			{				
				this.setResponseContext(routerResponseContext);
			}
		}
	  
		return this.convertResponse(routerResponse);
	}
	
	
	protected RS convertResponse(Object routerResponse)
	{
		if(routerResponse!=null) 
			return BeanUtils.bean2Bean(routerResponse, classRS);
		else
			return null;	
		
	}
	
	protected boolean updateState(){
		return true;
	}
	
	
	/**
	 * 
	 * orginRequestContext仅在录入时与当前请求上下文一样，在整个交易处理的生命周期里，orginRequestContext恒等于录入时的当前请求上下文
	 */
	final public RS notify(ProcessState processState, RR request, RequestContext originRequestContext) {
		RS defObj = null;
		switch(processState) {
			case I: //添加个性化业务日志
			{
				notifyInit(request, originRequestContext);
			}
			break;
			
			case SB: //提交交易
			{
				updateJnl(originRequestContext.getRequestJnlNo(), ProcessState.SB);
			    RS obj= notifySubmit(request, originRequestContext);
			    if(updateState())
			    {
			    	ProcessState ps=resolveProcessState(request);
			    	updateJnl(originRequestContext.getRequestJnlNo(), ps);
			    	getResponseContext().setResponseProcessState(ps);
			    }
			    return obj;
			}
			 
			default:
			{
				defObj = notifyOther(processState, request, originRequestContext);
			}
			break;
		}
		return defObj;
	}
	
	/**
	 * 交易自身解释状态
	 * @return
	 */
	protected ProcessState resolveProcessState(RR request){
		JNLState jnlState=JNLState.resolveJNLState(getResponseContext());
		ProcessState ps=ProcessState.OK;
	    if(jnlState==JNLState.F){
	    	ps=ProcessState.FL;
	    }else if(jnlState==JNLState.U){
	    	ps=ProcessState.UC;
	    }
	    return ps;
	}
	
	/**
	 * 从个性化日志恢复业务请求数据结构
	 */

	public RR restore(Long taskSeq) {
		if (getReadJnlSqlId() != null)
            return (RR) this.sqlMap.queryForObject(getReadJnlSqlId(), taskSeq);
        else if (this.getJnlDataService() != null)
            return (RR) jnlDataService.restore(taskSeq, "P", classRR);
        else
            return null;
	}
	
	protected void updateJnl(Long taskSeq,ProcessState processState){
		if(updateJnlSqlId==null) return;
		Map<Object, Object> param=new HashMap<Object, Object>();
		param.put("jnlNo", taskSeq);
		param.put("processState", processState);
		param.put("processTime", getRequestContext().getRequestTimestamp());
		this.sqlMap.update(updateJnlSqlId,param);
	}

	public void setJnlDataService(JNLDataService jnlDataService) {
		this.jnlDataService = jnlDataService;
	}

	public JNLDataService getJnlDataService() {
		return jnlDataService;
	}
}
